/* A sample Grading Calculator
 * A GradingCalculator class will have an array of users
 * Each user will have an array of terms
 * Each term will have an array of subjects
 * Each subject will have an array of Items
 * Each item will have a weight, and score.
 * 
 * The purpose of the GradingCalculator will be to automatically calculate the 
 * current grade and the possible outcome of predicted future grades. Also there 
 * wil be an option to calulate the score that you should get in order to meet a 
 * certain grade.
 * 
 * log:
 *  - next step is to update how to grade a user/subject/item
 *  - recently finished a print method to summarise a whole Grading Calculator
 */
package gradingcalculator;

public class GradingCalculator {

    public static void main(String[] args) {
        
        //Create the grading Calculator
        GradingCalculator sample = new GradingCalculator("Grader");
        //Add 2 Users named Marvin and Claire
        sample.addUser("Marvin");
        sample.addUser("Claire");
        
        //Add a Term in "Marvin" named "Spring Semester"
        sample.users[0].addTerm("Spring Semester");
        //Add a Subject in "Math" of 5 units
        sample.users[0].getTerm(0).addSubject("Math", 5);
        //Add an Grade Math at HW 
        sample.users[0].getTerm(0).getSubject(0).getItem(0).addInstance(100.0, 90.0);
        sample.users[0].getTerm(0).getSubject(0).getItem(0).addInstance(100.0, 95.0);
        sample.users[0].getTerm(0).getSubject(0).getItem(0).addInstance(100.0, 93.0);
        
        sample.printWhole();
        
                
    }
    
    //Prints (for testing on console programming)
    private void printWhole(){
        //Print Title
        System.out.println("Grading Calculator \"" + name + "\"");
        //Print the Users
        for(int i = 0; i<users.length; i++) {
            System.out.println("User " + (i+1) + ": " + users[i].getName());
            printTermsOfUser(users[i]);
        }
    }
    
    private void printTermsOfUser(User user) {
        if(user.getTerms() == null){
            System.out.println("- The Person \"" + user.getName() + "\" has no Terms.");
        }
        else{
            for(int i=0; i<user.getTerms().length; i++){
                System.out.println("- Term " + (i+1) + ": " + user.getTerm(i).getName());
                printSubjectsOfTerm(user.getTerm(i));
            }
        }
    }
    
    private void printSubjectsOfTerm(Term term){
        if(term.getSubjects() == null){
            System.out.println("- - \"" + term.getName() + "\" has no Subjects.");
        }
        else{
            for(int i=0; i<term.getSubjects().length; i++){
                System.out.println("- - Subject " + (i+1) + ": " + term.getSubject(i).getName() + " (" + term.getSubject(i).getUnits() + ") Grade: " +
                        gradeSubjectScore(term.getSubject(i)) + "/" + gradeSubjectMax(term.getSubject(i)) + " (" + gradeSubject(term.getSubject(i)) +
                        "%) Letter: " + calcLetterGrade(gradeSubject(term.getSubject(i))));
                printItemsOfSubject(term.getSubject(i));
            }
        }
    }
    
    private void printItemsOfSubject(Subject subject){
        if(subject.getItems() == null){
            System.out.println("- - - \"" + subject.getName() + "\" has no Items.");
        }
        else{
            for(int i=0; i<subject.getItems().length; i++){
                System.out.println("- - - Item " + (i+1) + ": " + subject.getItem(i).getName() + " (" + subject.getItem(i).getWeight() + "/%)");
                printInstancesOfItem(subject.getItem(i));
            }
        }
    }
    
    private void printInstancesOfItem(Item item){
        if(item.getInstances() == null){
            System.out.println("- - - - \"" + item.getName() + "\" has no Scores.");
        }
        else{
            for(int i=0; i<item.getInstances().length; i++){
                System.out.println("- - - - Score " + (i+1) + ": " + item.getInstance(i).grade() + "/" + item.getInstance(i).max());
            }
        }
    }
    
    //Attributes
    private String name;
    private User[] users;
    
    //Constructor
    public GradingCalculator(String name){
        this.name = name;
    }
    
    //Returns
    public String getName(){
        return name;
    }
    public User getUser(int i){
        return users[i];
    }
    public User[] getUsers(){
        return users;
    }
    
    //Modifiers
    public void setName(String name){
        this.name = name;
    }
    public void setUser(int i, User user){
        this.users[i] = user;
    }
    public void setUsers(User[] users){
        this.users = users;
    }
    
// ===== Advanced Modifiers (Adds, Counters, and Array Manipulators)============
    
    //ADDS
    public void addUser(String name){
        //Create an element in the array if the array is null
        if(users == null) { users = new User[1]; }
        //Otherwise add another element to the end of array
        else { expandUsersBy(1); }
        //Create a user in the Grading Calculator named 'name parameter'
        users[users.length-1] = new User(name);
    }
    
    //ARRAY MANIPULATOR
    private void expandUsersBy(int i) {
        User[] proxy = new User[users.length+i];
        System.arraycopy(users, 0, proxy, 0, users.length);
        users = proxy;
    }
    
    
    
// ===== Advanced Modifiers (Grade Calculations)================================
    
    // This method Counts how many 'instances' of the 'item' are 'graded'.
    public int countGraded(Item item){
        int count = 0;
        for(int i=0; i<item.getInstances().length; i++){
            if(item.getInstance(i).ifScored()){
                count +=1;
            }
        }
        return count;
    }
    
    // This method sums all the 'scores marked' of each 'instance' in the given 'item'.
    public double sumScoreMarked(Item item){
        double sum = 0;
        for(int i = 0; i<item.getInstances().length; i++){
            sum+=item.getInstance(i).grade();
        }
        return sum;
    }
    
    // This method sums all the 'maximum scores' of each 'graded instance' in the given 'item'.
    public double sumScoreGraded(Item item) {
        double sum = 0;
        for(int i = 0; i<item.getInstances().length; i++){
            if(item.getInstance(i).ifScored()){
                sum+=item.getInstance(i).grade();
            }
        }
        return sum;
    }
    
    // This method sums all the 'maximum scores' of all 'instance' in the given 'item'.
    public double sumScoreMax(Item item) {
        double sum = 0;
        for(int i = 0; i<item.getInstances().length; i++){
            sum+=item.getInstance(i).max();
        }
        return sum;
    }
    
    // This method calculates the 'grade' of all the 'graded scores' in the item.
    public double calcGrade(Item item) {
        double grade;
        grade = sumScoreMarked(item)/sumScoreMarked(item);
        return grade;
    }
    
    // This method calculates the 'grade' of all the 'scores including ungraded' in the item.
    public double calcGradeMax(Item item) {
        double grade;
        grade = sumScoreMarked(item)/sumScoreMarked(item);
        return grade;
    }
    
    // This method calculates the 'grade' of all the 'graded scores' in the subject.
    public double calcGrade(Subject subject) {
        double grade = 0;
        for(int i = 0; i < subject.getItems().length; i++){
            Item item = subject.getItem(i);
            grade+=(calcGrade(item)*item.getWeight()) / 100.00;
        }
        return grade;
    }
    
    // This method calculates the 'grade' of all the 'scores including ungraded' in the subject.
    public double calcGradeMax(Subject subject){
        double grade = 0;
        for(int i = 0; i < subject.getItems().length; i++){
            Item item = subject.getItem(i);
            grade+=(calcGradeMax(item)*item.getWeight()) / 100.00;
        }
        return grade;
    }
    
    //This method grades a subject, giving a
    public double gradeSubject(Subject subject) {
        double grade = 0;
        Item[] item = subject.getItems();
        for (int i = 0; i < item.length; i++) {
            Instance[] instance = item[i].getInstances();
            if(instance == null){
                
            }
            else{
                double sum = 0;
                double max = 0;
                double weight = item[i].getWeight();
                for(int j = 0; j < instance.length; j++){
                    if(instance[j].ifScored()){
                        sum+=instance[j].grade();
                        max+=instance[j].max();
                    }
                }
                try { grade += (sum/max)*weight; }
                catch(Exception e) { grade += 0; }
            }
        }
        return grade;
    }
    
    public double gradeSubjectScore(Subject subject) {
        double grade = 0;
        Item[] item = subject.getItems();
        for (int i = 0; i < item.length; i++) {
            Instance[] instance = item[i].getInstances();
            if(instance == null){
                
            }
            else{
                double sum = 0;
                double weight = item[i].getWeight();
                for(int j = 0; j < instance.length; j++){
                    if(instance[j].ifScored()){
                        sum+=instance[j].grade();
                    }
                }
                try { grade += (sum)*weight; }
                catch(Exception e) { grade += 0; }
            }
        }
        return grade;
    }
    
    public double gradeSubjectMax(Subject subject) {
        double grade = 0;
        Item[] item = subject.getItems();
        for (int i = 0; i < item.length; i++) {
            Instance[] instance = item[i].getInstances();
            if(instance == null){
                
            }
            else{
                double max = 0;
                double weight = item[i].getWeight();
                for(int j = 0; j < instance.length; j++){
                    if(instance[j].ifScored()){
                        max+=instance[j].max();
                    }
                }
                try { grade += (max)*weight; }
                catch(Exception e) { grade += 0; }
            }
        }
        return grade;
    }
    
    public double gradeSubjectMarked(Subject subject) {
        double grade = gradeSubjectScore(subject)/gradeSubjectMax(subject)*100;
        return grade;
    }
    
    public char calcLetterGrade(double grade){
        char letterGrade;
        if(grade >= 90.0){
            letterGrade = 'A';
        }
        else{
            if (grade >= 80.0){
                letterGrade = 'B';
            }
            else{
                if (grade >= 70.0){
                    letterGrade = 'C';
                }
                else{
                    letterGrade = 'F';
                }
            }
        }
        return letterGrade;
    }
    
}
